package org.gvp.generator.core;

import org.gvp.generator.core.channel.DefaultFileWriterChannel;
import org.gvp.generator.core.channel.FileWriterChannel;
import org.gvp.generator.core.writer.FileWriter;
import org.gvp.generator.log.LogFactory;
import org.gvp.generator.log.Logger;
import org.gvp.generator.props.CreateProps;
import org.gvp.generator.props.ProjectProps;
import org.gvp.generator.table.TableInfo;
import org.reflections.Reflections;

import java.io.FileNotFoundException;
import java.lang.reflect.InvocationTargetException;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Set;

public class FileWriterHandler {
    private final Logger log = LogFactory.getLogger(this.getClass());

    private ProjectProps project;
    private CreateProps create;
    private TableInfo tableInfo;

    public FileWriterHandler(ProjectProps project, CreateProps create, TableInfo tableInfo) {
        this.project = project;
        this.create = create;
        this.tableInfo = tableInfo;
    }

    public void write() {
        createDir();
        Set<Class<? extends FileWriter>> fileWriterClasses = getFileWriterClasses() ;
        fileWriterClasses.forEach(fw -> {
            WriterToResources annotation = fw.getAnnotation(WriterToResources.class);
            try {
                FileWriter fileWriter = fw.getConstructor().newInstance();
                if (fileWriter.isWriterFile(this.create)){
                    if (annotation == null){
                        log.debug("写入文件到类路径: " , fw.getName());
                        Path filePath = fileWriter.getFilePath(project.getJavaPath(), project, tableInfo.getUpperName());
                        writeFileHandler(fileWriter,filePath);
                    }else {
                        log.debug("写入文件到资源路径: " , fw.getName());
                        Path filePath = fileWriter.getFilePath(project.getResourcePath(), project, tableInfo.getUpperName());
                        writeFileHandler(fileWriter,filePath);
                    }
                }
            } catch (InstantiationException e) {
                log.debug("文件写入失败,实例化失败！");
                throw new RuntimeException(e);
            } catch (IllegalAccessException e) {
                log.debug("文件写入失败,没有权限！");
                throw new RuntimeException(e);
            } catch (InvocationTargetException e) {
                log.debug("文件写入失败,方法调用失败！");
                throw new RuntimeException(e);
            } catch (NoSuchMethodException e) {
                log.debug("文件写入失败,没有找到构造方法！");
                throw new RuntimeException(e);
            } catch (FileNotFoundException e) {
                log.debug("文件写入失败,文件不存在！");
                throw new RuntimeException(e);
            }
        });
    }

    private void writeFileHandler(FileWriter fw,Path filePath) throws FileNotFoundException {
        FileWriterChannel channel = new DefaultFileWriterChannel(filePath);
        log.debug("获取文件写入通道: " + fw.getClass().getSimpleName());
        fw.setFileWriterChannel(channel);
        fw.execute(this.tableInfo, this.project);
        log.debug("关闭文件写入通道: " + fw.getClass().getSimpleName());
        channel.close();
    }

    private void createDir() {
        if (create.isCreateDir()) {
            log.debug("创建系统需要文件夹信息");
            Paths.get(this.project.getJavaPath(), this.project.getEntityPackage()).toFile().mkdir();
            Paths.get(this.project.getJavaPath(), this.project.getMapperPackage()).toFile().mkdir();
            Paths.get(this.project.getJavaPath(), this.project.getRepositoryPackage()).toFile().mkdir();
            Paths.get(this.project.getJavaPath(), this.project.getRepositoryPackage(), "impl").toFile().mkdir();
            Paths.get(this.project.getJavaPath(), this.project.getServicePackage()).toFile().mkdir();
            Paths.get(this.project.getJavaPath(), this.project.getServicePackage(), "impl").toFile().mkdir();
            Paths.get(this.project.getJavaPath(), this.project.getControllerPackage()).toFile().mkdir();
            Paths.get(this.project.getResourcePath(), this.project.getXmlDirPath()).toFile().mkdir();
            Paths.get(this.project.getResourcePath(), this.project.getTypescriptDirPath()).toFile().mkdir();
        }
    }

    private Set<Class<? extends FileWriter>> getFileWriterClasses() {
        Reflections reflections = new Reflections("org.gvp.generator.core.writer.impl");
        Set<Class<? extends FileWriter>> subTypesOf = reflections.getSubTypesOf(FileWriter.class);
        log.debug("获取文件写入接口实现类信息: "  + subTypesOf.size());
        subTypesOf.forEach(e -> log.debug(e.getName()));
        return subTypesOf.stream().filter(e -> e.getName().startsWith("org.gvp.generator.core.writer.impl")).collect(java.util.stream.Collectors.toSet());
    }
}
